//+------------------------------------------------------------------+
//|                                                         6666.mq5 |
//|                                  Copyright 2025, MetaQuotes Ltd. |
//|                                             https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2025, MetaQuotes Ltd."  // 版权声明
#property link      "https://www.mql5.com"             // 官方链接
#property version   "1.00"                             // 版本号


string GlobalVarName = "加仓次数"; // 定义全局变量名称（中文便于识别），用于跨EA会话保存计数器值


// 输入参数 - 可在EA设置界面配置的参数
input ulong      InpMagicNumber = 123456;     // EA魔术编号（区分不同EA的唯一标识）
input string     InpNoTradeTimeStart = "19:30"; // 不交易时间段开始（服务器时间，格式HH:MM）
input string     InpNoTradeTimeEnd = "23:30";   // 不交易时间段结束（服务器时间，格式HH:MM）
input double     InpProfitMultiplier = 2.0;   // 盈利乘数（用于计算平仓目标价格）
input int        InpMAPeriod1 = 5;            // 第一条均线周期（用于技术分析）
input int        InpMAPeriod2 = 10;           // 第二条均线周期
input int        InpMAPeriod3 = 15;           // 第三条均线周期
input int        InpMAPeriod4 = 20;           // 第四条均线周期
input int        InpMAPeriod5 = 25;           // 第五条均线周期
input int        InpMAPeriod6 = 30;           // 第六条均线周期
input ENUM_APPLIED_PRICE InpMAAppliedPrice = PRICE_CLOSE; // 均线计算使用的价格（此处用收盘价）
input ENUM_MA_METHOD InpMAMethod = MODE_SMA;  // 均线计算方法（简单移动平均）
input int        InpGridStep = 30;           // 网格交易间距（以点为单位，决定加仓间隔）
input bool       InpEnableReverse = true;    // 是否启用反转功能
input int        InpReverseCycle = 3;         // 反转周期(默认为3)
input double     InpLotSize1 = 0.01;          // 第一次加仓手数
input double     InpLotSize2 = 0.01;          // 第二次加仓手数
// 后续加仓手数按顺序定义，支持最多15次加仓
input double     InpLotSize3 = 0.02;          // 第三次加仓手数（通常递增）
input double     InpLotSize4 = 0.04;          // 第四次加仓手数
input double     InpLotSize5 = 0.08;          // 第五次加仓手数
input double     InpLotSize6 = 0.52;          // 第六次加仓手数
input double     InpLotSize7 = 0.52;          // 第七次加仓手数
input double     InpLotSize8 = 1.04;          // 第八次加仓手数
input double     InpLotSize9 = 2.08;          // 第九次加仓手数
input double     InpLotSize10 = 4.16;         // 第十次加仓手数
input double     InpLotSize11 = 16.29;         // 第十一次加仓手数
input double     InpLotSize12 = 16.29;         // 第十二次加仓手数
input double     InpLotSize13 = 32.58;         // 第十三次加仓手数
input double     InpLotSize14 = 65.16;         // 第十四次加仓手数
input double     InpLotSize15 = 130.12;        // 第十五次加仓手数

// 全局变量 - 跨函数共享的数据
int maHandles[6];        // 存储6条均线指标的句柄（用于后续指标数据获取）
double maValues[6];      // 存储6条均线的当前值
datetime lastBarTime = 0;// 记录最后处理的K线时间（用于时间过滤）
bool filterEnabled = false; // 持仓过滤开关（控制是否启用持仓检查逻辑）
long chartID = 0;       // 当前图表ID（用于多图表环境区分）
int buttonId = 1;       // 按钮ID（预留UI交互使用）
int flage = -1;         // 多空方向标志（1=多头，0=空头，-1=未确定）
double lastOrderPrice = 0; // 最后一次开仓的价格（用于计算加仓间隔）
int orderCount = 0;     // 已执行的订单数量（记录加仓次数）
double lotSizes[15];    // 加仓手数数组（存储所有预设的加仓手数）

//+------------------------------------------------------------------+
//| EA初始化函数（启动时执行一次）                                   |
//+------------------------------------------------------------------+
int OnInit()
{
   // 初始化加仓手数数组：将输入参数按顺序存入数组，便于统一管理
   lotSizes[0] = InpLotSize1;
   lotSizes[1] = InpLotSize2;
   lotSizes[2] = InpLotSize3;
   lotSizes[3] = InpLotSize4;
   lotSizes[4] = InpLotSize5;
   lotSizes[5] = InpLotSize6;
   lotSizes[6] = InpLotSize7;
   lotSizes[7] = InpLotSize8;
   lotSizes[8] = InpLotSize9;
   lotSizes[9] = InpLotSize10;
   lotSizes[10] = InpLotSize11;
   lotSizes[11] = InpLotSize12;
   lotSizes[12] = InpLotSize13;
   lotSizes[13] = InpLotSize14;
   lotSizes[14] = InpLotSize15;
   
   // 打印初始化信息：显示魔术编号，用于确认EA实例
   Print("EA初始化 - 魔术编号: ", InpMagicNumber);


   // 检查全局变量是否存在（首次运行或被删除后不存在）
   if (!GlobalVariableCheck(GlobalVarName)) 
   {
	   // 不存在时初始化全局变量为0
	   GlobalVariableSet(GlobalVarName, 0);
	   Print("初始化加仓次数，值=0"); // 打印初始化日志
   }
   else 
   {
	   // 存在时获取当前值并打印（用于恢复历史计数）
	   Print("当前加仓次数=", GlobalVariableGet(GlobalVarName));
   }


   return(INIT_SUCCEEDED); // 返回初始化成功状态
}

//+------------------------------------------------------------------+
//| EA反初始化函数（卸载时执行）                                     |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
{
   // 打印卸载信息：记录魔术编号，便于日志追踪
   Print("EA已卸载，魔术编号: ", InpMagicNumber);

   // 示例：默认保留全局变量（注释掉删除代码）
   // 如需在EA卸载时清除计数器，取消下面注释：
   GlobalVariableDel(GlobalVarName); // 删除全局变量（可选功能）
}

//+------------------------------------------------------------------+
//| 行情更新处理函数（每个报价更新时触发）                            |
//+------------------------------------------------------------------+
void OnTick()
{
   flage=1; // 初始化方向标志为多头（实际逻辑可能根据策略调整）
   // 调用核心交易逻辑函数：马丁格尔网格交易策略
   MartinGridTrading();
}

//+------------------------------------------------------------------+
//| 持仓状态检查与恢复函数                                           |
//+------------------------------------------------------------------+
void CheckAndRestoreState()
{
   int total = PositionsTotal(); // 获取当前持仓总数
   Print("当前持仓",total);
   if(total == 0) 
   {

	  GlobalVariableSet(GlobalVarName, 0);
	  orderCount = 0;
      // 无持仓时跳过恢复流程
      Print("没有持仓，退出恢复状态检查,加仓次数归0");
	  
      return;
   }
   
   // 初始化持仓统计变量
   int buyPositions = 0;       // 多头持仓数量
   int sellPositions = 0;      // 空头持仓数量
   double buyVolume = 0;       // 多头总手数
   double sellVolume = 0;      // 空头总手数
   double lowestBuyPrice = DBL_MAX; // 最低多头开仓价（用于确定平均成本）
   double highestSellPrice = 0; // 最高空头开仓价（用于确定平均成本）
   
   // 遍历所有持仓
   for(int i = 0; i < total; i++)
   {
      ulong ticket = PositionGetTicket(i); // 获取持仓订单号
      if(PositionSelectByTicket(ticket))   // 选择当前持仓
      {
         string symbol = PositionGetString(POSITION_SYMBOL); // 获取品种名称
         if(symbol == _Symbol) // 仅处理当前图表品种
         {
            long type = PositionGetInteger(POSITION_TYPE); // 获取持仓类型（多/空）
            double volume = PositionGetDouble(POSITION_VOLUME); // 获取持仓手数
            double price = PositionGetDouble(POSITION_PRICE_OPEN); // 获取开仓价格
            
            if(type == POSITION_TYPE_BUY)
            {
               // 统计多头持仓
               buyPositions++;
               buyVolume += volume;
               // 更新最低多头开仓价（用于后续加仓判断）
               if(price < lowestBuyPrice) lowestBuyPrice = price;
            }
            else if(type == POSITION_TYPE_SELL)
            {
               // 统计空头持仓
               sellPositions++;
               sellVolume += volume;
               // 更新最高空头开仓价（用于后续加仓判断）
               if(price > highestSellPrice) highestSellPrice = price;
            }
         }
      }
   }

   if(GlobalVariableGet(GlobalVarName)==0)
   {
      GlobalVariableSet(GlobalVarName, 1);
   }
	orderCount = GlobalVariableGet(GlobalVarName);

   // 确定主要持仓方向
   if(buyPositions > 0 && sellPositions > 0)
   {
      // 同时存在多空持仓时视为异常状态
      Print("发现双向持仓，无法恢复状态");
      return;
   }
   else if(buyPositions > 0)
   {
      // 恢复多头状态：设置方向标志、记录最后开仓价和订单计数
      flage = 1;
      lastOrderPrice = lowestBuyPrice; // 以最低开仓价作为当前参考价格
      Print("恢复多头状态 - 最后开仓价: ", lastOrderPrice, " 订单计数: ", orderCount, " 总手数: ", buyVolume);
   }
   else if(sellPositions > 0)
   {
      // 恢复空头状态：设置方向标志、记录最后开仓价和订单计数
      flage = 0;
      lastOrderPrice = highestSellPrice; // 以最高开仓价作为当前参考价格
      Print("恢复空头状态 - 最后开仓价: ", lastOrderPrice, " 订单计数: ", orderCount, " 总手数: ", sellVolume);
   }
}

//+------------------------------------------------------------------+
//| 马丁网格交易核心逻辑函数                                         |
//+------------------------------------------------------------------+
void MartinGridTrading()
{
   // 首先检查并恢复持仓状态（处理EA重启后的状态同步）
   CheckAndRestoreState();
   
   // 方向未确定时跳过交易逻辑（初始状态或异常状态）
   if(flage == -1) return;  
   
   // 获取当前价格：多头用卖价（ASK），空头用买价（BID）
   double currentPrice = SymbolInfoDouble(_Symbol, flage == 1 ? SYMBOL_ASK : SYMBOL_BID);
   
   // 无持仓时开初始仓位
   if(PositionsTotal() == 0)
   {
      OpenInitialPosition(currentPrice); // 调用初始开仓函数
      return;
   }
   
   // 调用新增的加仓函数
   AddToPosition(currentPrice);

}
//+------------------------------------------------------------------+
//| 开初始仓位函数                                                   |
//+------------------------------------------------------------------+
void OpenInitialPosition(double price)
{
   // 重置订单计数和参考价格（初始仓位视为第0次加仓）
   GlobalVariableSet(GlobalVarName, 0);
   orderCount = 0;
   lastOrderPrice = price;
   
   // 使用第一组预设手数作为初始仓位
   double lotSize = lotSizes[0];
   
   // 构建交易请求结构
   MqlTradeRequest request = {};
   MqlTradeResult result = {};
   
   request.action = TRADE_ACTION_DEAL;       // 即时成交类型
   request.symbol = _Symbol;                 // 当前品种
   request.volume = lotSize;                 // 开仓手数
   request.type = (flage == 1) ? ORDER_TYPE_BUY : ORDER_TYPE_SELL; // 多空方向
   request.price = price;                    // 开仓价格
   request.deviation = 10;                   // 最大滑点（10点）
   request.magic = InpMagicNumber;           // 魔术编号（标识EA订单）
   request.comment = "首单：1";     // 订单注释
   
   // 发送交易请求
   if(!OrderSend(request, result))
   {
      // 处理开仓失败情况
      Print("开初始仓位失败, 错误码: ", GetLastError());
      return;
   }
   
   // 记录开仓成功信息
   Print("初始仓位已开: ", EnumToString(request.type), " 价格: ", price, " 手数: ", lotSize);
   orderCount++; // 订单计数+1（初始仓位计为第1次）
   GlobalVariableSet(GlobalVarName, orderCount);
   
}

//+------------------------------------------------------------------+
//| 加仓函数（新增部分）                                             |
//+------------------------------------------------------------------+
void AddToPosition(double currentPrice)
{
   double stepPoints = InpGridStep * _Point;
   bool conditionMet = false;
   
   int total = PositionsTotal(); // 获取当前持仓总数
   if(total == 0)
   {
      Print("没有持仓，无法加仓");
      GlobalVariableSet(GlobalVarName, 0);
      orderCount = 0;
      return; // 无持仓时跳过加仓逻辑
   } 

   // 计算价格差
   if(flage == 1) // 多头加仓条件
   {
      if(currentPrice <= lastOrderPrice - stepPoints)
      {
         conditionMet = true;
      }
   }
   else if(flage == 0) // 空头加仓条件
   {
      if(currentPrice >= lastOrderPrice + stepPoints)
      {
         conditionMet = true;
      }
   }
   
   if(!conditionMet) return;

   // 检查最大加仓次数
   if(orderCount >= ArraySize(lotSizes))
   {
      Print("已达到最大加仓次数");
      return;
   }

   if(orderCount % InpReverseCycle == 0 && orderCount==GlobalVariableGet(GlobalVarName) && InpEnableReverse == true)
   {
      printf("%d 能被 %d 整除。\n", orderCount, InpReverseCycle);
      CloseAllPositions(); // 平掉所有持仓
      flage = 1 - flage;
   }


   // 获取当前手数
   double lotSize = lotSizes[orderCount];
   
   // 创建交易请求
   MqlTradeRequest request = {};
   MqlTradeResult result = {};
   
   request.action = TRADE_ACTION_DEAL;
   request.symbol = _Symbol;
   request.volume = lotSize;
   request.type = (flage == 1) ? ORDER_TYPE_BUY : ORDER_TYPE_SELL;
   request.price = (flage == 1) ? SymbolInfoDouble(_Symbol, SYMBOL_ASK) : SymbolInfoDouble(_Symbol, SYMBOL_BID);
   request.deviation = 10;
   request.magic = InpMagicNumber;
   request.comment = "加仓 #" + IntegerToString(orderCount + 1);
   
   if(!OrderSend(request, result))
   {
      Print("加仓失败，错误码：", GetLastError());
      return;
   }
   
   // 更新状态
   lastOrderPrice = request.price;
   orderCount++;
   GlobalVariableSet(GlobalVarName, orderCount);
   
   Print("成功加仓：价格=", request.price, " 手数=", lotSize, " 总加仓次数=", orderCount);
}

//+------------------------------------------------------------------+
//| 平掉所有当前品种的持仓                                           |
//+------------------------------------------------------------------+
void CloseAllPositions()
{
   // 遍历所有持仓
   int total = PositionsTotal();
   for(int i = total-1; i >= 0; i--) // 从后往前遍历，因为平仓会改变持仓顺序
   {
      ulong ticket = PositionGetTicket(i);
      if(ticket > 0 && PositionGetString(POSITION_SYMBOL) == _Symbol)
      {
         // 获取持仓信息
         double volume = PositionGetDouble(POSITION_VOLUME);
         ENUM_POSITION_TYPE type = (ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE);
         
         // 创建平仓请求
         MqlTradeRequest request;
         ZeroMemory(request);
         request.action = TRADE_ACTION_DEAL;
         request.position = ticket;
         request.symbol = _Symbol;
         request.volume = volume;
         request.deviation = 5;
         request.magic = InpMagicNumber;
         
         // 设置平仓方向
         if(type == POSITION_TYPE_BUY)
            request.price = SymbolInfoDouble(_Symbol, SYMBOL_BID);
         else
            request.price = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
            
         request.type = (type == POSITION_TYPE_BUY) ? ORDER_TYPE_SELL : ORDER_TYPE_BUY;
         
         // 发送平仓请求
         MqlTradeResult result;
         ZeroMemory(result);
         if(OrderSend(request, result))
         {
            Print("成功平仓 #", ticket, " 手数: ", volume, " 类型: ", EnumToString(type));
         }
         else
         {
            Print("平仓失败 #", ticket, " 错误: ", GetLastError());
         }
      }
   }
}